#ifndef INC_MATH_UTILS_H
#define INC_MATH_UTILS_H

//////////////////////////////////////////////////////////////////////////////////////
// DECLARE USEFUL ENUMS
//////////////////////////////////////////////////////////////////////////////////////

#include <vector>//computeVolume

#include "Vector3.h"

namespace gti
{

struct triangle
{ 
    union
    {
        struct{
            unsigned short a,b,c;
        };
        unsigned short v[3];
    };
		const unsigned short& operator[](unsigned int i) const {return v[i];}
		unsigned short& operator[](unsigned int i) {return v[i];}
		const unsigned short& operator[](int i) const {return v[i];}
		unsigned short& operator[](int i) {return v[i];}

		operator unsigned short* (void) {return v;}
		operator const unsigned short* (void) const {return v;}

		void set(unsigned short ia, unsigned short ib, unsigned short ic)
		{
			a = ia; b = ib; c = ic;
		}

};

enum CullFlag
{
	INSIDE = 0,
	OUTSIDE,
	OVERLAP,
};

enum OctreeNodeId
{
	TOP_LEFT_FRONT = 0,			// 0
	TOP_LEFT_BACK,			// 1
	TOP_RIGHT_FRONT,
	TOP_RIGHT_BACK,			// etc...
	BOTTOM_LEFT_FRONT,
	BOTTOM_LEFT_BACK,
	BOTTOM_RIGHT_FRONT,
	BOTTOM_RIGHT_BACK
};

enum QuadtreeNodeId
{
	TOP_LEFT = 0,			// 0
	TOP_RIGHT,
	BOTTOM_LEFT,
	BOTTOM_RIGHT,
};

enum CubemapFace
{
	RIGHT=0,		// ( 1, 0, 0)
	TOP,			// ( 0, 1, 0)
	FRONT,			// ( 0, 0, 1)
	LEFT,			// (-1, 0, 0)
	BOTTOM,			// ( 0,-1, 0)
	BACK,			// ( 0, 0,-1)
	POSX = RIGHT,
	POSY = TOP,
	POSZ = FRONT,
	NEGX = LEFT,
	NEGY = BOTTOM,
	NEGZ = BACK,
	CUBEMAP_NONE, //used when no cubemap face is used
};

//////////////////////////////////////////////////////////////////////////////////////
// DECLARE USEFUL FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////
class matrix33;
class quaternion;
class AABB;

void Lerp(const vector3f &a, const vector3f &b, float percent,vector3f &c);
void Hermite(const vector3f &p0, const vector3f &p1,const vector3f &p2,const vector3f &p3, float t, vector3f& res);

float AngleBetweenVectors(const vector3f &v, const vector3f &w);
bool SameSide(const vector3f& p1, const vector3f&p2, const vector3f& a,const vector3f& b);

bool QuadraticFormula
(
	const float a,
	const float b,
	const float c,
	float& r1, //first
	float& r2 //and second roots
);

//////////////////////////////////////////////////////////////////////////////////////
// DECLARE USEFUL INTERSECTION FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////

bool PointInTriangle(//triangle a,b,c
	const vector3f& p,//point p
	const vector3f& a,// triangle vertex a
	const vector3f& b,// triangle vertex b
	const vector3f& c);// triangle vertex c

bool PointInQuad(
	const vector3f& p, 
	const vector3f& a,
	const vector3f& b,
	const vector3f& c,
	const vector3f& d);

bool PointInTriangleList(
	const vector3f& point,
	const vector3f* vertices,
	unsigned int verticesCnt);

bool SegmentOverlapAABB(const vector3f& midpoint, const vector3f& dir, const float halflength, const AABB& aabb);
bool SegmentOverlapAABB(const vector3f& p0, const vector3f& p1, const AABB& aabb);
bool FastSegmentOverlapAABB(
							const vector3f& a, //in global space
							const vector3f& b,// in global space
							const vector3f& center,
							const vector3f& halfsize);
bool FastSegmentOverlapAABB(
							const vector3f& v0,//in local space
							const vector3f& v1,//in local space
							const vector3f& halfsize );

// given the halfsize and orientation of an OBB, 
// rescale the halfsize to world coordinate frame
// (NOTE: the center is the same for both)
void OBBtoAABB(const vector3f& ihs, const quaternion& q, vector3f& ohs);
void OBBtoAABB(const vector3f& ihs, const matrix33& R, vector3f& ohs);

// OBB-OBB Intersection Test
bool OBBIntersectionTest(const vector3f& CA, const matrix33& RA, const vector3f& HA,
												 const vector3f& CB, const matrix33& RB, const vector3f& HB);
// AABB-AABB Intersection Test
bool AABBIntersectionTest(const vector3f& CA, const vector3f& HA, 
													const vector3f& CB, const vector3f& HB);

// Point-AABB test
bool PointAABBTest(const vector3f& point, const vector3f& center, const vector3f& halfsize);
//////////////////////////////////////////////////////////////////////////////////////
// DECLARE USEFUL DISTANCE FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////

void ClosestPointOnLine(//Line p0-p1
		const vector3f& p0,// point p0
		const vector3f& p1,// point p1
		const vector3f& p,//Point p
		vector3f& r);// r is closest point on line

void ClosestPointOnSegment(//Line p0-p1
		const vector3f& p0,// point p0
		const vector3f& p1,// point p1
		const vector3f& p,//Point p
		vector3f& r);// r is closest point on line

void ClosestPointOnTriangle(//Triangle a,b,c
		const vector3f& a,//triangle first vertex
		const vector3f& b,//triangle second vertex
		const vector3f& c,//triangle third vertex
		const vector3f& p,//point p
		vector3f& r);// r is closest point on triangle

// Returns Q0 and Q1 (closest point to l0 and l1)
void ClosestPointsBetweenSegments(
		const vector3f& P0,
		const vector3f& D0,
		const vector3f& P1,
		const vector3f& D1,
		vector3f& Q0,
		vector3f& Q1);

float EuclideanDistance(const vector3f& a, const vector3f& b);
float ManhattanDistance(const vector3f& a, const vector3f& b);


//////////////////////////////////////////////////////////////////////////////////////
// DECLARE USEFUL PARTITIONING FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////
void computeQuadtreeNodeVolume(char nodeID, const AABB& volume, AABB& childVolume);

void computeOctreeNodeVolume(char nodeID, const AABB& volume, AABB& childVolume);

//////////////////////////////////////////////////////////////////////////////////////
// ANGULAR FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////
float rad2deg( float rad );
float deg2rad( float deg);

#ifndef DEG_TO_RAD
#define DEG_TO_RAD 0.0174532925
#endif

#ifndef RAD_TO_DEG
#define RAD_TO_DEG 57.2957795
#endif

// polar to cartesians
vector3f angles2axis( float yaw, float pitch );

// cartesians to polar
void     axis2angles( const vector3f &axis, float *yaw, float *pitch );

// get just the yaw direction of the given vector
float    getAnglePlaneXZ( const vector3f &axis );

// returns a vector in the plance XZ with the given yaw angle
vector3f getVectorXZOfAngle( float yaw_angle );

// Rotates +90/-90 around the Y axis the given vector
vector3f rightOf( const vector3f &front );
vector3f leftOf( const vector3f &front );

// Returns a quaternion to rotate prev_dir to new_dir, up to the given ratio.
quaternion getRotationBetweenDirections( const vector3f &prev_dir, const vector3f &new_dir, float ratio = 1.0f );

#ifndef M_PI 
	#define M_PI       3.14159265358979323846
#endif

template <class T>
T clamp(const T value, const T mi, const T ma)
{
	if (value < mi) return mi;
	else if (value > ma) return ma;
	return value;
}

void computeVolume(unsigned int count, const gti::vector3f* vertices, gti::AABB& volume);
void computeVolume(const std::vector<gti::vector3f>& vertices, gti::AABB& volume);

};//namespace gti

#endif//INC_MATH_UTILS_H
